/*
 * Copyright (C) 2005-2015 Schlichtherle IT Services.
 * All rights reserved. Use is subject to license terms.
 */

#if($licenseKeyFormat == "V2/JSON")
#set($licenseApplicationContextPackage = "org.truelicense.v2.json")
#set($licenseApplicationContextClass = "V2JsonLicenseApplicationContext")
#elseif($licenseKeyFormat == "V2/XML")
#set($licenseApplicationContextPackage = "org.truelicense.v2.xml")
#set($licenseApplicationContextClass = "V2XmlLicenseApplicationContext")
#elseif($licenseKeyFormat == "V1")
#set($licenseApplicationContextPackage = "org.truelicense.v1")
#set($licenseApplicationContextClass = "V1LicenseApplicationContext")
#end
#set($editions = $editions.split($delimiter))
#set($editionsSize = $editions.size())
#set($hash = '#')
#set($dollar = '$')
#set($english = $field.get("java.util.Locale.ENGLISH"))
#macro(protection $string)$java.obfuscatedString($string).replaceAll("new org.truelicense.obfuscate.ObfuscatedString(.*)", "protection$1")#end
#macro(new $class $interface)
#if($customClasspathScope == "runtime")
new Object() {
    $interface newInstance() {
        try {
            return ($interface) Class.forName("$class").newInstance();
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new IllegalArgumentException(e);
        }
    }
}.newInstance()
#else
new ${class.replace('$', '.')}()
#end
#end
/* Generated from Velocity template at $date - DO NOT EDIT! */
package ${package}.keygen;

import javax.security.auth.x500.X500Principal;
import org.truelicense.api.*;
import org.truelicense.api.io.Sink;
import org.truelicense.api.passwd.PasswordProtection;
import $licenseApplicationContextPackage.$licenseApplicationContextClass;
import org.truelicense.obfuscate.*;

/**
 * The enumeration of the vendor license managers for $subject license keys.
 * The managers are named like each configured edition and ordered from
 * superset to subset.
 * Each manager is configured with the algorithms and parameters for generating
 * license keys for the respective edition.
 * <p>
 * This class is immutable and hence trivially thread-safe.
 *
 * @author Christian Schlichtherle
 */
public enum LicenseManager implements VendorLicenseManager {
#foreach($edition in $editions)
#set($editionKeyEntryAlias = $edition)
#evaluate("${hash}set (${dollar}editionKeyEntryAlias = ${dollar}${edition}KeyEntryAlias)")
#set($editionKeyEntryPassword = $privateKeyStorePassword)
#evaluate("${hash}set (${dollar}editionKeyEntryPassword = ${dollar}${edition}KeyEntryPassword)")
#set($editionPbePassword = $password)
#evaluate("${hash}set (${dollar}editionPbePassword = ${dollar}${edition}PbePassword)")

    $edition {
#if($editionKeyEntryAlias == $edition)
#set($editionKeyEntryAlias = "name()")
#else
        @Obfuscate
        private static final String KEY_ENTRY_ALIAS = "$editionKeyEntryAlias";

#set($editionKeyEntryAlias = "KEY_ENTRY_ALIAS")
#end
        @Override
        VendorLicenseManager newManager() {
            return _managementContext.vendor()
                    .encryption()
#if($licenseKeyFormat == "V1" && $pbeAlgorithm != "PBEWithMD5AndDES" ||
    $licenseKeyFormat.startsWith("V2/") && $pbeAlgorithm != "PBEWithSHA1AndDESede")
                        .algorithm(PBE_ALGORITHM)
#end
                        .protection(#protection($editionPbePassword))
                        .inject()
                    .authentication()
                        .alias($editionKeyEntryAlias)
#if($editionKeyEntryPassword != $privateKeyStorePassword)
                        .keyProtection(#protection($editionKeyEntryPassword))
#end
                        .loadFromResource(KEY_STORE_FILE)
                        .storeProtection(#protection($privateKeyStorePassword))
#if($licenseKeyFormat == "V1" && $privateKeyStoreType != "JKS" ||
    $licenseKeyFormat.startsWith("V2/") && $privateKeyStoreType != "JCEKS")
                        .storeType(KEY_STORE_TYPE)
#end
                        .inject()
                    .build();
        }
    }#if($foreach.count == $editionsSize);#{else},#end

#end

    @Obfuscate
    private static final String DISTINGUISHED_NAME = "$distinguishedName";

    @Obfuscate
    private static final String KEY_STORE_FILE = "$privateKeyStoreFile";
#if($licenseKeyFormat == "V1" && $privateKeyStoreType != "JKS" ||
    $licenseKeyFormat.startsWith("V2/") && $privateKeyStoreType != "JCEKS")

    @Obfuscate
    private static final String KEY_STORE_TYPE = "$privateKeyStoreType";
#end

    @Obfuscate
    private static final String SUBJECT = "$subject";
#if($licenseKeyFormat == "V1" && $pbeAlgorithm != "PBEWithMD5AndDES" ||
    $licenseKeyFormat.startsWith("V2/") && $pbeAlgorithm != "PBEWithSHA1AndDESede")

    @Obfuscate
    private static final String PBE_ALGORITHM = "$pbeAlgorithm";
#end

    // Note that the class implementing the interface
    // org.truelicense.api.LicenseManagementContext
    // determines the format of the license keys:
    // The implementation class
    // $licenseApplicationContextPackage.$licenseApplicationContextClass
    // uses the license key format $licenseKeyFormat.
    private static final $licenseApplicationContextClass _applicationContext =
            new $licenseApplicationContextClass();

    private static final LicenseManagementContext _managementContext =
            _applicationContext
                .context()
#if($keyGenAuthorizationClass != "-")
                    .authorization(#new($keyGenAuthorizationClass "LicenseManagementAuthorization"))
#end
                    .initialization(new LicenseInitialization() {
                            @Override
                            public void initialize(final License bean) {
                                if (null == bean.getIssuer())
                                    bean.setIssuer(new X500Principal(DISTINGUISHED_NAME));
                            }
                        })
                    .subject(SUBJECT)
#if($keyGenValidationClass != "-")
                    .validation(#new($keyGenValidationClass "LicenseValidation"))
#if($keyGenValidationComposition != "decorate")
                    .validationComposition(LicenseFunctionComposition.$keyGenValidationComposition)
#end
#end
                    .build();

    private volatile VendorLicenseManager _manager;

    private VendorLicenseManager manager() {
        // No need to synchronize because managers are virtually stateless.
        final VendorLicenseManager m = _manager;
        return null != m ? m : (_manager = newManager());
    }

    abstract VendorLicenseManager newManager();

    final PasswordProtection protection(long[] obfuscated) {
        return _applicationContext.protection(obfuscated);
    }

    @Override
    public LicenseKeyGenerator generator(License bean) {
        return manager().generator(bean);
    }

    @Override
    public LicenseManagementContext context() {
        assert _managementContext == manager().context();
        return _managementContext;
    }

    @Override
    public LicenseManagementParameters parameters() {
        return manager().parameters();
    }
}
